#ifndef ASSERT_H
#define ASSERT_H

#include <iostream>
#include <string>
#include <sstream>
#include <vector>

using namespace std;

struct AssertFailedException {
	string type;
	string expected;
	string actual;
	string file;
	long line;
};
std::ostream& operator << (std::ostream& out, const AssertFailedException ex);
bool operator == (const AssertFailedException e1, AssertFailedException e2);

#define ASSERT_EQUALS(expected, actual) \
	 assertEquals(expected, actual, __FILE__, __LINE__)

template <typename ParamType>
void assertEquals(ParamType expected, ParamType actual, string file, long line) {
	if (!(expected == actual)) {
		AssertFailedException ex;
		ex.type = "AssertEquals";

		stringstream expectedStream;
		expectedStream << expected;
		ex.expected = expectedStream.str();

		stringstream actualStream;
		actualStream << actual;
		ex.actual = actualStream.str();

		ex.file = file;
		ex.line = line;

		throw ex;
	}
}

template<class T, template<class U, class = allocator<U> > class Seq>
void assertEquals(Seq<T> expectedSeq, Seq<T> actualSeq, string file, long line) {
	if (!(expectedSeq == actualSeq)) {
		AssertFailedException ex;
		ex.type = "AssertEqualsSequence";

		stringstream expectedStream;
		for(Seq<T>::iterator it = expectedSeq.begin(); it != expectedSeq.end(); it++) {
			if (!expectedStream.str().empty()) {
				expectedStream << " ";
			}
			expectedStream << *it;
		}
		ex.expected = expectedStream.str();

		stringstream actualStream;
		for(Seq<T>::iterator it = actualSeq.begin(); it != actualSeq.end(); it++) {
			if (!actualStream.str().empty()) {
				actualStream << " ";
			}
			actualStream << *it;
		}
		ex.actual = actualStream.str();

		ex.file = file;
		ex.line = line;

		throw ex;
	}
}

#define ASSERT_NULL(object) \
	 assertNull(object, __FILE__, __LINE__)
void assertNull(void* object, string file, long line);

#define ASSERT_NOT_NULL(object) \
	 assertNotNull(object, __FILE__, __LINE__)
void assertNotNull(void* object, string file, long line);

#define ASSERT_TRUE(value) \
	 assertTrue(value, __FILE__, __LINE__)
void assertTrue(bool value, string file, long line);

#define ASSERT_FALSE(value) \
	 assertFalse(value, __FILE__, __LINE__)
void assertFalse(bool value, string file, long line);

string intToString(int i);

#endif